using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Project9
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        private GraphicsDeviceManager graphics;
        private SpriteBatch spriteBatch;

        private SpriteFont font;

        private Texture2D projectile;

        private Texture2D axis;
        private Texture2D cross;
        private Texture2D dot;
        private Texture2D dot2;

        private Vector2 offset;

        private Vector2 ballPos1;
        private Vector2 ballPos2;

        private String other;

        private InputProcessor input;

        private bool isStop = true;

        private float time = 0;

        private float mass1 = 1;
        private float mass2 = 1;

        private float e = 0;

        private Vector2 initV;
        private Vector2 initU;
        private Vector2 finalV;
        private Vector2 finalU;

        private float TOI;
        private int count =0;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            graphics.PreferredBackBufferHeight = 800;
            graphics.PreferredBackBufferWidth = 800;
            IsMouseVisible = true;

            input = new InputProcessor(this);

            Components.Add(input);
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            font = Content.Load<SpriteFont>("font");

            projectile = Content.Load<Texture2D>("projectile");
            CreateRectangle(10, 3, out cross, Color.Red);
            CreateRectangle(800, 2, out axis, Color.Black);
            CreateRectangle(2, 2, out dot, Color.Red);
            CreateRectangle(3, 3, out dot2, Color.Blue);

            ballPos1 = new Vector2(-380, 0);
            ballPos2 = new Vector2(80, 0);

            initU = new Vector2(100, 0);
            initV = Vector2.Zero;

            offset = new Vector2(400, 400);


        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();

            // TODO: Add your update logic here
            if (input.isKeyPressed(Keys.Space)) isStop = !isStop;
            if (input.isKeyPressed(Keys.Back))
            {
                isStop = true;
                time = 0;
                ballPos1 = new Vector2(-380, 0);
                ballPos2 = new Vector2(80, 0);

                count = 0;

            }

            if (isStop && time == 0)
            {
                if (input.isKeyPressed(Keys.Q)) mass1++;
                if (input.isKeyPressed(Keys.A)) mass1--;

                if (input.isKeyPressed(Keys.W)) mass2++;
                if (input.isKeyPressed(Keys.S)) mass2--;

                if (input.isKeyPressed(Keys.E)) initU.X += 10;
                if (input.isKeyPressed(Keys.D)) initU.X -= 10;

                if (input.isKeyPressed(Keys.R)) initV.X += 10;
                if (input.isKeyPressed(Keys.F)) initV.X -= 10;

                if (input.isKeyPressed(Keys.T)) e += 0.1f;
                if (input.isKeyPressed(Keys.G)) e -= 0.1f;

                mass1 = MathHelper.Clamp(mass1, 1, 10);
                mass2 = MathHelper.Clamp(mass2, 1, 10);

                initU.X = MathHelper.Clamp(initU.X, 50, 150);
                initV.X = MathHelper.Clamp(initV.X, -50, 10);

                TOI = (ballPos1.X - ballPos2.X + 56) / (initV.X - initU.X);
            }


            float J = -(initU.X - initV.X) * (e + 1) * mass1 * mass2 / (mass1 + mass2);
            finalU.X = J / mass1 + initU.X;
            finalV.X = -J / mass2 + initV.X;

            if (!isStop)
            {
                time *= 1000;
                time += gameTime.ElapsedGameTime.Milliseconds;
                time /= 1000;

                if (time < TOI)
                {
                    ballPos1.X += (float)(initU.X * gameTime.ElapsedGameTime.TotalSeconds);
                    ballPos2.X += (float)(initV.X * gameTime.ElapsedGameTime.TotalSeconds);
                }
                else
                {
                    count = 1;
                    ballPos1.X += (float)(finalU.X * gameTime.ElapsedGameTime.TotalSeconds);
                    ballPos2.X += (float)(finalV.X * gameTime.ElapsedGameTime.TotalSeconds);
                }

            }



            other = "M1=" + mass1 + "kg, M2=" + mass2 + "kg\n\n"
                + "Initial: u=" + initU.X + "m/s, v=" + initV.X + "m/s\n"
                + "Final: u=" + finalU.X + "m/s, v=" + finalV.X + "m/s\n\n"
                + "e=" + e
                + "\nImpulse J=" + J + "kg.m/s\n"
                + "Collision Count=" + count
                + "\n\np_i=" + (mass1 * initU.X) + "+" + (mass2 * initV.X) + "=" + (mass1 * initU.X + mass2 * initV.X) + "kg.m/s\n"
                + "p_f=" + (mass1 * finalU.X) + "+" + (mass2 * finalV.X) + "=" + (mass1 * finalU.X + mass2 * finalV.X) + "kg.m/s";


            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin();
            spriteBatch.Draw(axis, offset, null, Color.White, 0f, new Vector2(400, 1), 1f, SpriteEffects.None, 0f);
            spriteBatch.Draw(axis, offset, null, Color.White, MathHelper.PiOver2, new Vector2(400, 1), 1f, SpriteEffects.None, 0f);





            spriteBatch.Draw(projectile, ballPos1 + offset, null, Color.White, 0, new Vector2(20, 20), 1f, SpriteEffects.None, 0f);
            spriteBatch.Draw(projectile, ballPos2 + offset, null, Color.White, 0, new Vector2(20, 20), 1f, SpriteEffects.None, 0f);

            spriteBatch.DrawString(font, other, new Vector2(10, 500), Color.White);

            spriteBatch.End();

            base.Draw(gameTime);
        }

        public void CreateRectangle(int width, int height, out Texture2D rectangleTexture, Color color)
        {
            // create the rectangle texture, ,but it will have no color! lets fix that
            rectangleTexture = new Texture2D(GraphicsDevice, width, height, false, SurfaceFormat.Color);

            // set the color to the amount of pixels
            Color[] colors = new Color[width * height];

            // loop through all the colors setting them to whatever values we want
            for (int i = 0; i < colors.Length; i++)
            {
                colors[i] = color;
            }

            // set the color data on the texture
            rectangleTexture.SetData(colors);

        }
    }
}
